﻿using Activity.Redis.Helper;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Activity.Redis
{
    /// <summary>
    /// Redis客户端
    /// </summary>
    public class RedisClient : IDisposable
    {
        private static ConfigurationOptions _configuration;
        /// <summary>
        /// 从Web.config中获取Redis配置信息
        /// </summary>
        protected static ConfigurationOptions Configuration
        {
            get
            {
                var server = ConfigurationHelper.Get("redis-server", "192.168.88.102");
                var port = ConfigurationHelper.Get("redis-server-port", 6379);
                var pass = ConfigurationHelper.Get("redis-server-pass", "1qazxcvfr4");
                //var server = ConfigurationHelper.Get("redis-server", "121.41.101.1");
                //var port = ConfigurationHelper.Get("redis-server-port", 44444);
                //var pass = ConfigurationHelper.Get("redis-server-pass", "1QAZ2wsxheyue");
                return _configuration ?? (
                _configuration = new ConfigurationOptions()
                {
                    AbortOnConnectFail = false,
                    ConnectTimeout = 1000,
                    ResponseTimeout = 1000,
                    EndPoints = { { server, port } },
                    Password = pass
                });
            }

        }
        private static readonly Lazy<ConnectionMultiplexer> LazyConnection = new Lazy<ConnectionMultiplexer>(() => {
            return ConnectionMultiplexer.Connect(Configuration);
        });
        /// <summary>
        /// 创建一个Redis连接
        /// </summary>
        public static ConnectionMultiplexer Connection
        {
            get
            {
                return LazyConnection.Value;
            }
        }
        /// <summary>
        /// 返回一个Reids服务器
        /// </summary>
        public static IServer Server
        {
            get
            {
                var ep = Connection.GetEndPoints(true);
                return Connection.GetServer(ep.FirstOrDefault());
            }
        }

        public int DataBaseIndex
        {
            get;
            protected set;
        }
        private IDatabase _database;
        /// <summary>
        /// 返回一个数据库
        /// </summary>
        /// <returns></returns>
        public IDatabase GetDatabase()
        {
            return _database ?? (_database = Connection.GetDatabase(this.DataBaseIndex));
        }
        /// <summary>
        /// 初始化客户端
        /// </summary>
        /// <param name="index">要打开的数据库（0-15），默认-1，超出有效访问自动修改为-1</param>
        public RedisClient(int index = -1)
        {
            this.DataBaseIndex = Math.Max(Math.Min(index, 15), -1);
        }

        #region Redis 基本数据的读写操作
        /// <summary>
        /// 添加一个字符串到Redis
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="minutes"></param>
        public RedisClient AddString(string key, string value, int minutes = 0)
        {
            var db = this.GetDatabase();
            if (minutes > 0)
                db.StringSet(key, value, TimeSpan.FromMinutes(minutes));
            else
                db.StringSet(key, value);
            return this;
        }
        /// <summary>
        /// 添加一个字符串到Redis
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="minutes"></param>
        public RedisClient AddLong(string key, long value, int minutes = 0)
        {
            var db = this.GetDatabase();
            if (minutes > 0)
                db.StringSet(key, value, TimeSpan.FromMinutes(minutes));
            else
                db.StringSet(key, value);
            return this;
        }
        #endregion


        public void SetIntList(Dictionary<string, int> list)
        {
            var db = this.GetDatabase();
            foreach (var key in list.Keys)
            {
                db.StringSet(key, list[key]);
            }
        }
        public void SetInt(string key, int value)
        {
            var db = this.GetDatabase();
            db.StringSet(key, value);
        }
        public RedisClient SetString(string key, RedisValue value, int minutes)
        {
            var db = this.GetDatabase();
            db.StringSet(key, value, TimeSpan.FromMinutes(minutes));
            return this;
        }
        public RedisClient SetString(string key, RedisValue value, TimeSpan timeSpan)
        {
            var db = this.GetDatabase();
            db.StringSet(key, value, timeSpan);
            return this;
        }
        public RedisClient SetString(string key, RedisValue value)
        {
            var db = this.GetDatabase();
            db.StringSet(key, value);
            return this;
        }
        /// <summary>
        /// 添加字符串到SET
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public RedisClient AddStringToSet(string key, string value)
        {
            var db = this.GetDatabase();
            db.SetAdd(key, value);
            return this;
        }
        public RedisClient SetDecimal(string key, decimal value)
        {
            var db = this.GetDatabase();
            db.StringSet(key, value.ToString());
            return this;
        }
        public RedisClient SetStrString(string key, string value)
        {
            var db = this.GetDatabase();
            db.StringSet(key, value);
            return this;
        }
        public RedisClient SetString(string key, DateTime value)
        {
            var db = this.GetDatabase();
            db.StringSet(key, value.Ticks);
            return this;
        }
        public RedisClient SetString(string key, DateTime value, int minutes)
        {
            var db = this.GetDatabase();
            db.StringSet(key, value.Ticks, TimeSpan.FromMinutes(minutes));
            return this;
        }
        /// <summary>
        /// 重置超时时间
        /// </summary>
        /// <param name="key"></param>
        /// <param name="minutes"></param>
        public RedisClient Expire(string key, int minutes)
        {
            var db = this.GetDatabase();
            db.KeyExpire(key, TimeSpan.FromMinutes(minutes));
            return this;
        }
        /// <summary>
        /// 判断 item 是否存在SET（key）中
        /// </summary>
        /// <param name="key"></param>
        /// <param name="item"></param>
        /// <returns></returns>
        public bool IsItemExists(string key, RedisValue item)
        {
            var db = this.GetDatabase();
            return db.SetContains(key, item);
        }
        /// <summary>
        /// 判断 item 是否存在SET（key）中
        /// </summary>
        /// <param name="key"></param>
        /// <param name="item"></param>
        /// <returns></returns>
        public bool IsIntInSet(string key, int item)
        {
            return IsMember(key, item);
        }
        /// <summary>
        /// 判断 item 是否存在SET（key）中
        /// </summary>
        /// <param name="key"></param>
        /// <param name="item"></param>
        /// <returns></returns>
        public bool IsStringSet(string key, string item)
        {
            return IsMember(key, item);
        }

        /// <summary>
        /// 从Redis中读取 二进制数据（byte[]）
        /// </summary>
        /// <param name="key"></param>
        /// <param name="dv"></param>
        /// <returns></returns>
        public byte[] GetBuffer(string key, byte[] dv)
        {
            var db = this.GetDatabase();
            return db.KeyExists(key)
                ? (byte[])db.StringGet(key)
                : dv;
        }
        public RedisValue GetString(string key, RedisValue dv)
        {
            var db = this.GetDatabase();
            return db.KeyExists(key) ? db.StringGet(key) : dv;
        }
        public RedisValue GetString(string key)
        {
            var db = this.GetDatabase();
            return db.StringGet(key);
        }
        /// <summary>
        /// 从Redis中读取一个字符串
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public string GetStringValue(string key)
        {
            var db = this.GetDatabase();
            return db.StringGet(key);
        }
        /// <summary>
        /// 判断Key是否存在
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public bool KeyExists(string key)
        {
            var db = this.GetDatabase();
            return db.KeyExists(key);
        }
        public int GetInt(string key, int dv = 0)
        {
            var db = this.GetDatabase();
            if (!db.KeyExists(key))
            {
                return dv;
            }
            var r = db.StringGet(key);
            if (!r.HasValue)
            {
                return dv;
            }
            var i = 0;
            r.TryParse(out i);
            return i;
        }

        public double GetDouble(string key, double dv = 0)
        {
            var db = this.GetDatabase();
            if (!db.KeyExists(key))
            {
                return dv;
            }
            var r = db.StringGet(key);
            if (!r.HasValue)
            {
                return dv;
            }
            var i = 0D;
            r.TryParse(out i);
            return i;
        }

        public long GetLong(string key, long dv = 0)
        {
            var db = this.GetDatabase();
            if (!db.KeyExists(key))
            {
                return dv;
            }
            var r = db.StringGet(key);
            if (!r.HasValue)
            {
                return dv;
            }
            var i = 0L;
            r.TryParse(out i);
            return i;
        }
        public decimal GetDecimal(string key, decimal dv = 0)
        {
            var db = this.GetDatabase();
            if (!db.KeyExists(key))
            {
                return dv;
            }
            var r = db.StringGet(key);
            if (!r.HasValue)
            {
                return dv;
            }
            var i = 0M;
            if (decimal.TryParse(r.ToString(), out i))
            {
                return i;
            }
            return dv;
        }
        //

        public int GetAndMinuInt(string key, int dv = 0)
        {
            var db = this.GetDatabase();
            if (!db.KeyExists(key))
            {
                return dv;
            }
            var r = db.StringGet(key);
            if (!r.HasValue)
            {
                return dv;
            }
            var i = 0;
            if (r.TryParse(out i))
                db.StringSet(key, i - 1);
            return i;

        }

        /// <summary>
        /// 向一个LIST添加数据
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public long AddValueToList(string key, RedisValue value)
        {
            var db = this.GetDatabase();
            return db.ListRightPush(key, value);
        }
        /// <summary>
        /// 向一个LIST添加字符串
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public long AddStringToList(string key, string value)
        {
            var db = this.GetDatabase();
            return db.ListRightPush(key, value);
        }
        /// <summary>
        /// 添加Int到List
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public long AddIntToList(string key, int value)
        {
            var db = this.GetDatabase();
            return db.ListRightPush(key, value);
        }
        /// <summary>
        /// 批量添加Int到List
        /// </summary>
        /// <param name="key"></param>
        /// <param name="values"></param>
        /// <returns></returns>
        public long AddIntToList(string key, IEnumerable<int> values)
        {
            var db = this.GetDatabase();
            return db.ListRightPush(key, values.Select(i => (RedisValue)i).ToArray());
        }

        /// <summary>
        /// 获取LIST
        /// </summary>
        /// <param name="key"></param>
        /// <param name="start"></param>
        /// <param name="stop"></param>
        /// <returns></returns>
        public RedisValue[] GetList(string key, long start = 0L, long stop = -1L)
        {
            var db = this.GetDatabase();
            return db.ListRange(key, start, stop);
        }
        /// <summary>
        /// 从List中读取一个Int值
        /// </summary>
        /// <param name="key"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        public int GetIntFromList(string key, int index)
        {
            var db = this.GetDatabase();
            return (int)db.ListGetByIndex(key, index);
        }
        /// <summary>
        /// 修改 List 中 的数据
        /// </summary>
        /// <param name="key"></param>
        /// <param name="index"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public RedisClient SetIntToList(string key, int index, int value)
        {
            var db = this.GetDatabase();
            db.ListSetByIndex(key, index, 0);
            return this;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        /// <param name="start"></param>
        /// <param name="stop"></param>
        /// <returns></returns>
        public IList<string> GetStringFromList(string key, long start = 0L, long stop = -1L)
        {
            var db = this.GetDatabase();
            return db.ListRange(key, start, stop).Select(s => s.ToString()).ToList();
        }

        /// <summary>
        /// 返回一个LIST的“长度”
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public long GetListLength(string key)
        {
            var db = this.GetDatabase();
            return db.ListLength(key);
        }
        /// <summary>
        /// 返回一个SET的“长度”
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public long GetSetLength(string key)
        {
            var db = this.GetDatabase();
            return db.SetLength(key);
        }

        /// <summary>
        /// 统计关键字（Key）的数量
        /// </summary>
        /// <param name="pattern"></param>
        /// <returns></returns>
        public long Count(string pattern)
        {
            long result = 0;
            var server = Server;
            IScanningCursor cursor;
            var keys = server.Keys(DataBaseIndex, pattern, 1000).ToList();
            do
            {
                result += keys.LongCount();
                cursor = keys as IScanningCursor;
                if (cursor != null)
                {
                    keys = server.Keys(DataBaseIndex, pattern, cursor.PageSize, cursor.Cursor, cursor.PageOffset).ToList();
                }
            } while (cursor != null && cursor.Cursor > 0 && keys.LongCount() > 0);
            return result;
        }

        public IEnumerable<string> FindKey(string pattern, int count)
        {
            var server = Server;
            if (count < 1000)
                return server.Keys(DataBaseIndex, pattern).Select(i => i.ToString());
            var result = new List<string>();
            IScanningCursor cursor;
            var keys = server.Keys(DataBaseIndex, pattern, 1000)
                .Select(i => i.ToString());
            do
            {
                result.AddRange(keys);
                cursor = keys as IScanningCursor;
                if (cursor != null)
                {
                    keys = server.Keys(DataBaseIndex, pattern, cursor.PageSize, cursor.Cursor, cursor.PageOffset)
                        .Select(i => i.ToString());
                }
            } while (cursor != null && cursor.Cursor > 0);
            return result;
        }
        public IEnumerable<RedisKey> GetKeys(string pattern, int count)
        {
            var server = Server;
            if (count < 1000) return server.Keys(DataBaseIndex, pattern);
            var result = new List<RedisKey>();
            IScanningCursor cursor;
            var keys = server.Keys(DataBaseIndex, pattern, 1000);
            do
            {
                result.AddRange(keys);
                cursor = keys as IScanningCursor;
                if (cursor != null)
                {
                    keys = server.Keys(DataBaseIndex, pattern, cursor.PageSize, cursor.Cursor, cursor.PageOffset);
                }
            } while (cursor != null && cursor.Cursor > 0);
            return result;
        }
        /// <summary>
        /// 搜索关键字
        /// </summary>
        /// <param name="pattern"></param>
        /// <param name="page"></param>
        /// <param name="limit"></param>
        /// <returns></returns>
        public IEnumerable<RedisKey> GetKeys(string pattern, int page, int limit)
        {
            var server = Server;
            var keys = server.Keys(DataBaseIndex, pattern, limit, 0, page);
            return keys;
        }

        #region LIST
        public long RemoveStringFromList(string pKey, string pValue)
        {
            var db = this.GetDatabase();
            return db.ListRemove(pKey, pValue);
        }
        #endregion

        #region Hash

        public RedisClient HashSet(string hashKey, string field, RedisValue value)
        {
            var db = this.GetDatabase();
            db.HashSet(hashKey, field, value);
            return this;
        }

        public RedisValue HashGet(string hashKey, string field, RedisValue dv)
        {
            if (!KeyExists(hashKey)) return dv;
            var db = this.GetDatabase();
            if (!db.HashExists(hashKey, field)) return dv;
            return db.HashGet(hashKey, field);
        }
        public RedisValue HashGet(string hashKey, int field, RedisValue dv)
        {
            if (!KeyExists(hashKey)) return dv;
            var db = this.GetDatabase();
            if (!db.HashExists(hashKey, field)) return dv;
            return db.HashGet(hashKey, field);
        }
        #endregion

        #region 集合（SET）

        public IEnumerable<RedisValue> GetValuesFromSet(string key, int skip, int limit)
        {
            var db = this.GetDatabase();
            if (!KeyExists(key)) throw new Exception(key + " 不存在");
            if (db.KeyType(key) != RedisType.Set)
            {
                throw new Exception(key + " 已经存在，当不是SET");
            }
            return db.SetScan(key, pageSize: limit, cursor: skip);
        }
        /// <summary>
        /// 从SET读取字符串，返回列表
        /// </summary>
        /// <param name="key"></param>
        /// <param name="skip"></param>
        /// <param name="limit"></param>
        /// <returns></returns>
        public IEnumerable<string> GetStringFromSet(string key, int skip, int limit)
        {
            var db = this.GetDatabase();
            if (!KeyExists(key)) throw new Exception(key + " 不存在");
            if (db.KeyType(key) != RedisType.Set)
            {
                throw new Exception(key + " 已经存在，当不是SET");
            }
            return db.SetScan(key, pageSize: limit, cursor: skip).Select(i => (string)i);
        }
        /// <summary>
        /// 从SET中读取 Int 列表
        /// </summary>
        /// <param name="key"></param>
        /// <param name="skip"></param>
        /// <param name="limit"></param>
        /// <returns></returns>
        public IEnumerable<int> GetIntFromSet(string key, int skip, int limit)
        {
            var db = this.GetDatabase();
            if (!KeyExists(key)) throw new Exception(key + " 不存在");
            if (db.KeyType(key) != RedisType.Set)
            {
                throw new Exception(key + " 已经存在，当不是SET");
            }
            return db.SetScan(key, pageSize: limit, cursor: skip).Select(i => (int)i);
        }

        /// <summary>
        /// 把一个值从集合中移除
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public RedisClient RemoveValueFromSet(string key, RedisValue value)
        {
            var db = this.GetDatabase();
            if (!KeyExists(key))
                return this;
            if (db.KeyType(key) != RedisType.Set)
            {
                throw new Exception(key + " 已经存在，当不是SET");
            }
            db.SetRemove(key, value);
            return this;
        }
        /// <summary>
        /// 把一个值从集合中移除
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public RedisClient RemoveStringFromSet(string key, string value)
        {
            return RemoveValueFromSet(key, value);
        }

        /// <summary>
        /// 把一个 Int 值从集合中移除
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public RedisClient RemoveIntFromSet(string key, int value)
        {
            return RemoveValueFromSet(key, value);
        }
        /// <summary>
        /// 添加值到一个集合
        /// </summary>
        /// <param name="key">
        /// 如果数据库已经存在这个key但是不是SET类型则报错
        /// </param>
        /// <param name="value"></param>
        public void AddValueToSet(string key, RedisValue value)
        {
            var db = this.GetDatabase();
            if (KeyExists(key))
            {
                if (db.KeyType(key) != RedisType.Set)
                {
                    throw new Exception(key + " 已经存在，且不是SET");
                }
            }
            db.SetAdd(key, value);
        }
        /// <summary>
        /// 添加值到一个集合
        /// </summary>
        /// <param name="key">
        /// 如果数据库已经存在这个key但是不是SET类型则报错
        /// </param>
        /// <param name="value"></param>
        public void AddIntToSet(string key, int value)
        {
            var db = this.GetDatabase();
            if (KeyExists(key))
            {
                if (db.KeyType(key) != RedisType.Set)
                {
                    throw new Exception(key + " 已经存在，且不是SET");
                }
            }
            db.SetAdd(key, value);
        }
        /// <summary>
        /// 判断一个值是否包含在一个集合里面
        /// </summary>
        /// <param name="key">
        /// 如果给定的key不是一个SET报错
        /// </param>
        /// <param name="value"></param>
        /// <returns></returns>
        public bool IsMember(string key, RedisValue value)
        {
            var db = GetDatabase();
            if (!KeyExists(key))
                return false;
            if (db.KeyType(key) != RedisType.Set)
            {
                return false;
            }
            return db.SetContains(key, value);
        }

        #endregion

        #region Remove
        /// <summary>
        /// 根据匹配模式删除KEY
        /// </summary>
        /// <param name="pattern"></param>
        public long RemoveKeysByPattern(string pattern)
        {
            var ids = Server.Keys(this.DataBaseIndex, pattern);
            var db = this.GetDatabase();
            return db.KeyDelete(ids.ToArray());
        }

        /// <summary>
        /// 从数据库中删除值（按key来删除）
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public bool RemoveKey(string key)
        {
            var db = this.GetDatabase();
            return db.KeyDelete(key);
        }
        /// <summary>
        /// 从数据库中删除一组key，不存在的key自动忽略
        /// </summary>
        /// <param name="keys"></param>
        public void RemoveKeys(IEnumerable<string> keys)
        {
            var db = this.GetDatabase();
            var ids = keys.Select(s => (RedisKey)s).ToArray();
            db.KeyDelete(ids);
        }
        /// <summary>
        /// 从数据库中删除一组key，不存在的key自动忽略
        /// </summary>
        /// <param name="keys"></param>
        public void RemoveKeys(IEnumerable<RedisKey> keys)
        {
            var db = this.GetDatabase();
            db.KeyDelete(keys.ToArray());
        }
        /// <summary>
        /// 从数据库中删除一组key，不存在的key自动忽略
        /// </summary>
        /// <param name="keys"></param>
        public void RemoveKeys(RedisKey[] keys)
        {
            var db = this.GetDatabase();
            db.KeyDelete(keys);
        }
        #endregion

        public long MinusIncrement(string key, int pValue)
        {
            var db = GetDatabase();
            var r = db.StringDecrement(key, pValue);
            return r;
        }

        public long PlusIncrement(string key, int pValue)
        {
            var db = GetDatabase();
            var r = db.StringIncrement(key, pValue);
            return r;
        }

        public object Test()
        {
            var db = this.GetDatabase();
            if (db.KeyExists("110_1_2"))
            {
                var r = db.StringGet("110_1_2");
                if (r.HasValue)
                {
                    var i = 0;
                    r.TryParse(out i);
                    i++;
                    db.StringSet("110_1_2", i);
                    return i;
                }
                return false;
            }
            return db.StringSet("110_1_2", 134);
        }

        public void Dispose()
        {
            _configuration = null;
            _database = null;
        }


    }
}
